
package VideoRent.Entrada;

import java.io.BufferedReader;
import java.util.regex.*;
import java.util.Calendar;
import java.lang.NumberFormatException;

import VideoRent.*;
import VideoRent.Cliente.ClienteAsociado;
import VideoRent.Enum.EstadoCliente;

public class ClientesAsociados extends VideoRentEntrada
{
    //Elementos de la tienda donde se registran los nuevos clientes
    private ElementosTienda elementosTienda;
    //Expresión regular que analiza la cabecera de los clientes
    private Pattern patronCabecera = null;
    //Expresión regular que analiza un cliente
    private Pattern patronCliente = null;


    /**
     * Constructor del lector de clientes asociados
     * 
     * @param elementosTienda Objeto donde se registran todos los clientes
     *                        nuevos que se van asoaciado
     */
    public ClientesAsociados(ElementosTienda elementosTienda){
        this.elementosTienda = elementosTienda;
    }


    /**
     * Lee todo el contenido de un archivo de clientes asociados y lo carga
     * a los elementos de la tienda. Realiza las debidas validaciones sobre
     * el formato del archivo.
     * 
     * @param bufferLectura Buffer del archivo que contiene los clientes asociados
     */
    public void leer(BufferedReader bufferLectura)
        throws VideoRentEntradaException
    {
        String linea = leerLinea(bufferLectura);
        if (null == linea){
            throw new VideoRentEntradaException("Archivo mal formateado: no vino la primera linea con la cantidad de clientes y el próximo identificador");
        }

        Matcher buscador = getPatronCabecera().matcher(linea);

        if (!buscador.find()){
            throw new VideoRentEntradaException("Archivo mal formateado: primera línea en formato incorrecto '" + linea + "'");
        }

        int totalClientesAsociados;
        try{
            totalClientesAsociados = Integer.parseInt(buscador.group(1));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Total de clientes asociados inválido: '" + buscador.group(1) + "'");
        }

        try{
            ClienteAsociado.setSiguienteCodigo(Integer.parseInt(buscador.group(2)));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Código siguiente correlativo de cliente inválido: '" + buscador.group(2) + "'");
        }

        for (int i = 0; i < totalClientesAsociados; ++ i){
            linea = leerLinea(bufferLectura);
            if (null == linea){
                throw new VideoRentEntradaException("Se esperaban '" + totalClientesAsociados + "' clientes. Se encontraron '" + i + "'");
            }

            buscador = getPatronCliente().matcher(linea);
            if (!buscador.find()){
                throw new VideoRentEntradaException("La linea '" + linea + "' no pudo ser analizada correctamente");
            }

            agregarCliente(buscador);
        }
    }


    /**
     * Genera el patrón compilado de la expresión regular que analiza
     * la cabecera. Se asegura de generarla sólo una vez.
     * 
     * @return Expresión regular compilada que analiza la cabecera
     */
    protected Pattern getPatronCabecera(){
        if (null == patronCabecera){
            //Cantidad de clientes y próximo código correlativo
            patronCabecera = Pattern.compile("^(\\d+)\\s*(\\d+)$");
        }
        return patronCabecera;
    }


    /**
     * Dada una coincidencia de expresión regular de cliente asociado,
     * formatea la información y genera un cliente. Realiza validaciones
     * de unicidad y semántica.
     * 
     * @param buscadorCliente Objeto que contiene la coincidencia de línea y
     *                        los resultados de la información del cliente
     */
    protected void agregarCliente(Matcher buscadorCliente)
        throws VideoRentEntradaException
    {
        if (null != elementosTienda.buscarClienteAsociado(buscadorCliente.group(1))){
            throw new VideoRentEntradaException("El cliente con el código '" + buscadorCliente.group(1) + "' ya fue asociado");
        }

        int cedula;
        try{
            cedula = Integer.parseInt(buscadorCliente.group(3));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Cédula inválida: '" + buscadorCliente.group(3) + "' para el cliente '" + buscadorCliente.group(1) + "'");
        }

        ClienteAsociado c = elementosTienda.buscarClienteAsociado(cedula);
        if (null != c){
            throw new VideoRentEntradaException("El cliente con el código '" + buscadorCliente.group(1) +
                "' tiene una cédula '" + cedula + "' que ya está registrada al código '" + c.getCodigo() + "'");
        }

        EstadoCliente estado;
        if ("suspendido".equals(buscadorCliente.group(2))){
            estado = EstadoCliente.SUSPENDIDO;
        }else if ("activo".equals(buscadorCliente.group(2))){
            estado = EstadoCliente.ACTIVO;
        }else{
            throw new VideoRentEntradaException("Estado de cliente inválido: '" + buscadorCliente.group(2) + "' para el cliente '" + buscadorCliente.group(1) + "'");
        }

        int telefono;
        try{
            telefono = Integer.parseInt(buscadorCliente.group(6));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Teléfono inválido: '" + buscadorCliente.group(6) + "' para el cliente '" + buscadorCliente.group(1) + "'");
        }

        int cvv;
        try{
            cvv = Integer.parseInt(buscadorCliente.group(10));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Código de seguridad inválido: '" + buscadorCliente.group(10) + "' para el cliente '" + buscadorCliente.group(1) + "'");
        }

        int mesVencimiento;
        try{
            mesVencimiento = Integer.parseInt(buscadorCliente.group(11));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Mes de fecha de vencimiento de tarjeta de crédito: '" + buscadorCliente.group(11) + "' para el cliente '" + buscadorCliente.group(1) + "'");
        }

        int anoVencimiento;
        try{
            anoVencimiento = Integer.parseInt(buscadorCliente.group(12));
        }catch(NumberFormatException e){
            throw new VideoRentEntradaException("Año de fecha de vencimiento de tarjeta de crédito: '" + buscadorCliente.group(12) + "' para el cliente '" + buscadorCliente.group(1) + "'");
        }
        Calendar fechaVencimiento = Calendar.getInstance();
        fechaVencimiento.set(anoVencimiento, mesVencimiento, 1, 0, 0, 0);

        elementosTienda.agregarAsociado(new ClienteAsociado(cedula, estado,
            //Nombre
            buscadorCliente.group(4),
            //Apellido
            buscadorCliente.group(5), telefono,
            //Código
            buscadorCliente.group(1),
            //Dirección fiscal
            buscadorCliente.group(7),
            //Número de tarjeta
            buscadorCliente.group(8),
            //Banco
            buscadorCliente.group(9), cvv, fechaVencimiento));
    }


    /**
     * Genera el patrón compilado de la expresión regular que analiza
     * una línea de cliente. Se asegura de generarla sólo una vez.
     * 
     * @return Expresión regular compilada que analiza el cliente
     */
    protected Pattern getPatronCliente(){
        if (null == patronCliente){
            patronCliente = Pattern.compile(
                //Código
                "^((?:P|B)\\d+)\\s*" +
                //Estado
                "&\\s*(activo|suspendido)\\s*" +
                //Cédula
                "&\\s*(\\d+)\\s*" +
                //Nombre
                "&\\s*(\\w+)\\s*" +
                //Apellido
                "&\\s*(\\w+)\\s*" +
                //Teléfono
                "&\\s*(\\d+)\\s*" +
                //Dirección
                "&\\s*([^&]+?)\\s*" +
                //Tarjeta de crédito
                "&\\s*(\\d+)\\s*" +
                //Banco
                "&\\s*([^&]+?)\\s*" +
                //Código de seguridad
                "&\\s*(\\d{3})\\s*" +
                //Fecha de vencimiento
                "&\\s*0?(\\d{1,2})/(\\d{4})\\s*$");
        }
        return patronCliente;
    }
}